package com.mall.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.commonservice.enums.RedisValueTTLEnum;
import com.mall.application.api.ApplicationApi;
import com.mall.application.enums.ApplicationStautsEnum;
import com.mall.application.enums.RedisKeyPrefixEnum;
import com.mall.application.mapper.ApplicationMapper;
import com.mall.application.entity.po.ApplicationPO;
import com.mall.application.entity.param.ApplicationAddParam;
import com.mall.application.entity.param.ApplicationEditParam;
import com.mall.application.entity.vo.ApplicationVO;
import com.mall.common.enums.ResponseEnum;
import com.mall.common.exception.BizException;
import com.starters.redisson.config.RedissonDistributedLocker;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;

/**
 * @author wuyijie
 * @description: TODO 应用管理api实现类
 * @date 2021/6/12 10:52
 */
@DubboService
@Slf4j
public class ApplicationApiImpl implements ApplicationApi {
    @Autowired
    private ApplicationMapper applicationMapper;
    @Autowired
    @Qualifier("applicationRedisTemplate")
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private RedissonDistributedLocker redissonDistributedLocker;

    @Override
    public int add(ApplicationAddParam applicationAddParam) {
        String lockerKey = RedisKeyPrefixEnum.LOCKER_APPLICATION_NAME.getPerfix() + applicationAddParam.getName();
        try {
            redissonDistributedLocker.tryLock(lockerKey);
            int add;
            ApplicationVO applicationVO = this.queryByName(applicationAddParam.getName());
            if (ObjectUtil.isNull(applicationVO)) {
                ApplicationPO applicationPO = BeanUtil.toBean(applicationAddParam, ApplicationPO.class);
                applicationPO.setAccessKey(IdUtil.simpleUUID());
                applicationPO.setAccessSecret(IdUtil.simpleUUID());
                applicationPO.setStatus(ApplicationStautsEnum.USING.getStatus());

                add = applicationMapper.insert(applicationPO);
                return add;
            } else {
                throw new BizException(ResponseEnum.OBJECT_ALREAD_EXIST_ERROE);
            }
        } catch (Exception e) {
            throw new BizException(ResponseEnum.INTERNAL_SERVER_ERROR, e);
        } finally {
            redissonDistributedLocker.unLock(lockerKey);
        }

    }

    @Override
    public int deleteById(int id) {
        String lockerKey = RedisKeyPrefixEnum.LOCKER_APPLICATION_ID.getPerfix() + id;
        try {
            redissonDistributedLocker.tryLock(lockerKey);
            int delete;
            ApplicationVO applicationVO = this.queryById(id);

            if (ObjectUtil.isNotNull(applicationVO)) {
                ApplicationPO applicationPO = new ApplicationPO();
                applicationPO.setStatus(ApplicationStautsEnum.DELETED.getStatus());

                delete = applicationMapper.updateById(applicationPO);
                this.deleteApplicationRedisCache(applicationPO);
                return delete;
            } else {
                throw new BizException(ResponseEnum.OBJECT_ALREAD_EXIST_ERROE);
            }

        } catch (Exception e) {
            throw new BizException(ResponseEnum.INTERNAL_SERVER_ERROR, e);
        } finally {
            redissonDistributedLocker.unLock(lockerKey);
        }
    }

    @Override
    public int edit(ApplicationEditParam applicationEditParam) {
        String lockerKey = RedisKeyPrefixEnum.LOCKER_APPLICATION_ID.getPerfix() + applicationEditParam.getId();
        try {
            redissonDistributedLocker.tryLock(lockerKey);

            int edit;
            ApplicationVO applicationVO = this.queryById(applicationEditParam.getId());
            ApplicationVO newApplicationVO = this.queryByName(applicationVO.getName());

            if (ObjectUtil.isNull(newApplicationVO)) {
                ApplicationPO applicationPO = BeanUtil.toBean(applicationEditParam, ApplicationPO.class);
                edit = applicationMapper.updateById(applicationPO);

                this.deleteApplicationRedisCache(applicationPO);
            } else {
                throw new BizException(ResponseEnum.OBJECT_ALREAD_EXIST_ERROE);
            }
            return edit;
        } catch (Exception e) {
            throw new BizException(ResponseEnum.INTERNAL_SERVER_ERROR, e);
        } finally {
            redissonDistributedLocker.unLock(lockerKey);
        }

    }

    @Override
    public int pause(int id) {
        String lockerKey = RedisKeyPrefixEnum.LOCKER_APPLICATION_ID.getPerfix() + id;

        try {
            redissonDistributedLocker.tryLock(lockerKey);
            int edit;

            ApplicationVO applicationVO = this.queryById(id);

            if (ObjectUtil.isNotNull(applicationVO)) {
                applicationVO.setStatus(ApplicationStautsEnum.PAUSED.getStatus());
                ApplicationPO applicationPO = BeanUtil.toBean(applicationVO, ApplicationPO.class);

                edit = applicationMapper.updateById(applicationPO);

                this.deleteApplicationRedisCache(applicationPO);
            } else {
                throw new BizException(ResponseEnum.OBJECT_ALREAD_EXIST_ERROE);
            }
            return edit;
        } catch (Exception e) {
            throw new BizException(ResponseEnum.INTERNAL_SERVER_ERROR, e);
        } finally {
            redissonDistributedLocker.unLock(lockerKey);
        }

    }

    @Override
    public ApplicationVO queryById(int id) {
        String redisKey = RedisKeyPrefixEnum.APPLICATION_ID.getPerfix() + id;
        ApplicationVO applicationVO = null;
        if (redisTemplate.hasKey(redisKey)) {
            String applicationJSONString = redisTemplate.opsForValue().get(redisKey);
            ApplicationPO applicationPO = JSONObject.toJavaObject(JSONObject.parseObject(applicationJSONString), ApplicationPO.class);
            applicationVO = BeanUtil.toBean(applicationPO, ApplicationVO.class);

        } else {
            LambdaQueryWrapper<ApplicationPO> applicationPOLambdaQueryWrapper = new LambdaQueryWrapper<>();
            applicationPOLambdaQueryWrapper.eq(ApplicationPO::getId, id);
            applicationPOLambdaQueryWrapper.eq(ApplicationPO::getStatus, ApplicationStautsEnum.USING.getStatus());

            ApplicationPO applicationPO = applicationMapper.selectOne(applicationPOLambdaQueryWrapper);
            if (ObjectUtil.isNotNull(applicationPO)) {
                applicationVO = BeanUtil.toBean(applicationPO, ApplicationVO.class);
                this.setApplicationRedisCache(applicationPO);
            }
        }
        return applicationVO;
    }

    @Override
    public ApplicationVO queryByName(String name) {
        String redisKey = RedisKeyPrefixEnum.APPLICATION_NAME.getPerfix() + name;
        ApplicationVO applicationVO;
        if (redisTemplate.hasKey(redisKey)) {
            String applicationJSONString = redisTemplate.opsForValue().get(redisKey);
            ApplicationPO applicationPO = JSONObject.toJavaObject(JSONObject.parseObject(applicationJSONString), ApplicationPO.class);
            applicationVO = BeanUtil.toBean(applicationPO, ApplicationVO.class);
        } else {
            LambdaQueryWrapper<ApplicationPO> applicationPOLambdaQueryWrapper = new LambdaQueryWrapper<>();
            applicationPOLambdaQueryWrapper.eq(ApplicationPO::getName, name);
            applicationPOLambdaQueryWrapper.eq(ApplicationPO::getStatus, ApplicationStautsEnum.USING.getStatus());

            ApplicationPO applicationPO = applicationMapper.selectOne(applicationPOLambdaQueryWrapper);
            applicationVO = BeanUtil.toBean(applicationPO, ApplicationVO.class);

            this.setApplicationRedisCache(applicationPO);
        }

        return applicationVO;
    }

    void deleteApplicationRedisCache(ApplicationPO applicationPO) throws RuntimeException{
        redisTemplate.delete(RedisKeyPrefixEnum.APPLICATION_ID.getPerfix() + applicationPO.getId());
        redisTemplate.delete(RedisKeyPrefixEnum.APPLICATION_NAME.getPerfix() + applicationPO.getName());
    }

    void setApplicationRedisCache(ApplicationPO applicationPO) throws RuntimeException{
        String redisValue = JSONObject.toJSONString(applicationPO);
        redisTemplate.opsForValue().set(RedisKeyPrefixEnum.APPLICATION_ID.getPerfix() + applicationPO.getId(), redisValue, RedisValueTTLEnum.MINUTE_5.getTime(), TimeUnit.MINUTES);
        redisTemplate.opsForValue().set(RedisKeyPrefixEnum.APPLICATION_NAME.getPerfix() + applicationPO.getName(), redisValue, RedisValueTTLEnum.MINUTE_5.getTime(), TimeUnit.MINUTES);

    }

}
